-
-
Notifications
You must be signed in to change notification settings - Fork 404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Re-enable option system keyword validation #1277
Conversation
The approach taken here seems reasonable and I'll be glad to see some validation again. The only thing I'd request is that at minimum we show the |
41b515e
to
ad97744
Compare
That all sounds good. After some thought, validating against only the loaded backends does seem to be the right thing to do. That way, if someone changes a notebook from mpl to bokeh, they'll immediately be warned about the options that no longer apply. And if they want to make a notebook that works for both backends, they have a way to declare that options for both will be appearing. The only valid use case I can see that's not covered is if someone wants to write a notebook valid for multiple backends, but only ever use one backend at any one time (i.e., no per-cell backend switching), and doesn't want the other backends not in use to be loaded at all. But that seems like a rare case and a reasonable limitation, while trying to separate out the validation from the other backend code seems like it would be infeasible, so I agree with this approach. |
dfb9f52
to
84fb568
Compare
@jbednar I agree with your analysis.
That does seem to be an odd thing to do. Why would you write a notebook for multiple backends but insist only one is imported at a time?
The plotting backends define the plotting classes which declare their allowed keywords i.e allowed plot and style options (as parameters and a class attribute). In theory you could make a 'mock' backend with stub plotting classes that only have parameters and this attribute, allowing you ro declare what holoviews supports for a given backend without actually importing it. It would be fair bit of effort (for little to no benefit) but it would be possible to declare everything needed for keyword validation/opts magic completion without importing anything external. My point is that the options system isn't inherently tied to anyone else's code so I'm not sure why anyone would want to do this! |
@philippjfr Out of curiosity, couldn't this work already? Trying to import the bokeh backend could still register all the plotting classes and style options even if bokeh isn't available. If we wanted, we could get that option tree even if things would then break were you to actually try to use those plotting classes. Again, just curious - I see no real reason to do this! |
Seems like it would be a right pain wrapping all the bokeh imports. |
Agreed. I'm saying if we wanted to this, it wouldn't be an insane amount of work, just annoying. Thankfully we aren't doing this - I'm just wondering about the space of possibilities. :-) |
Note that this PR will rely on a fix in #1285 - currently if you try to load only the bokeh backend, the matplotlib backend is also loaded, breaking |
Well, the same reason one would write code that works with both Python 2 and 3. Just because we write our code to work with both, we don't then insist that people have both 2 and 3 installed to use it; they just use the code in whatever context makes sense for them. Similarly, one might plausibly want to write a notebook that supports both mpl and Bokeh, distributing it for various people to consume according to their own preference, without requiring everyone to have both backends installed.
Agreed; I'm glad it could be done if it somehow became a priority, but there's no need to do that work in the foreseeable future. |
What I might be able to do right away is this:
Then we can accommodate the scenario you mentioned by simply requiring |
Assuming False means "warn when invalid options are detected" (not "abort when invalid options are detected), then yes, I think the default should clearly be False.
Right. I think that's all true, though it means giving up on option validation entirely in that case, so probably what the developer should do then is to leave the option False during development, loading both backends but allowing options to be validated, then distribute with the option True to suppress warnings on user systems. Seems like a reasonable compromise. |
That currently isn't the case but I don't see why I can't change it to make it the way you describe - currently it raises an |
Actually I think this is an issue in the implementation, not the parameters: skip_invalid = param.Boolean(default=True, doc="""
Whether all Options instances should skip invalid keywords or
raise and exception. May only be specified at the class level.""")
warn_on_skip = param.Boolean(default=True, doc="""
Whether all Options instances should generate warnings when
skipping over invalid keywords or not. May only be specified at
the class level.""") I suggest we just set the default of Make sense? Edit 1: The docstrings would need to be updated... |
Yes that makes sense to me. |
Yes, sounds good. Regarding the name change, I think the possible combinations here should act like the following when an invalid option is encountered:
If that's true, then the names should be |
34c7d1a
to
455d2bd
Compare
fc2ba39
to
7997094
Compare
@philippjfr I think this PR is ready for review. It didn't turn out as ugly as I had anticipated and the new Here is an example of what happens: In the first case, We can worry about how we want |
override_kwargs = dict(options.kwargs) | ||
old_allowed = group_options.allowed_keywords | ||
override_kwargs['allowed_keywords'] = options.allowed_keywords + old_allowed | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Three lines that ruined both our productivity for several hours, yay!
@@ -314,7 +382,7 @@ def __call__(self, allowed_keywords=None, **kwargs): | |||
""" | |||
Create a new Options object that inherits the parent options. | |||
""" | |||
allowed_keywords=self.allowed_keywords if allowed_keywords is None else allowed_keywords | |||
allowed_keywords=self.allowed_keywords if allowed_keywords in [None,[]] else allowed_keywords |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One line that ruined productivity for an evening! hoorah!
Very glad that we're finally reenabling validation. Thanks for taking the hit on this, hunting down that bug was more than enough for me. Going to merge. |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
This PR attempts to re-enable option system keyword validation, addressing issue #1101.
Before HoloViews supported multiple backends, we put a lot of effort into keyword validation. However, once we had multiple backends, it became problematic to have keywords in the opts magic applicable to some other backend other than the current one. As a result, validation was disabled entirely.
One issue is that the new form of validation needs to work across backends. I think the sensible thing is to validate against the loaded backends as you don't want to import all possible backends to see what keywords might make sense.
The next issue is more fundamental - our original validation system was based around a custom
OptionError
exception that knew about appropriate keywords. The idea was if something was invalid, raise an exception and present it to the user appropriately.The issue now is that exceptions disrupt the code and each
OptionError
was specified per backend. What we want now is to figure out keywords that make sense for none of the loaded backends. In other words you need to take a union, and normally if you raise an exception you will only get one exception at a time.The solution shown here is to allow
StoreOptions
to record the exception objects that are currently being silently ignored. This lets me collect all the invalid option reports together - if a particular key is reported as invalid for all loaded backends, I assume it really is invalid and reraise anOptionError
with it, using the union of all theallowed_keywords
across backends. I hope that makes sense!One positive thing about this approach is that practically everything is implemented as classmethods on
StoreOptions
with almost zero changes to theOptions
andOptionTrees
which are pretty hard to understand as it is.That said, I'm not entirely sure this approach is 100% correct as it doesn't look at which element complained or what type of option complained (i.e plot or style). This is probably fixable by declaring the
Options
with a bit more (optional!) information such as the element name and backend. This shouldn't be too bad as the plotting code doesn't actually raiseOptionError
, instead they declare their allowable plot and style keywords which are used to buildOption
instances in theStore.register
classmethod. As everything is declared consistently in one place, we should be able to supplyOptions
andOptionErrors
with the appropriate metadata to do the correct union operation.Hope that makes sense!